《深度学习入门》第4章 神经网络的学习 您所在的位置:网站首页 loss减小 acc减小 《深度学习入门》第4章 神经网络的学习

《深度学习入门》第4章 神经网络的学习

2023-04-20 03:16| 来源: 网络整理| 查看: 265

1 第4章整体思路

神经网络的学习:神经网络存在合适的权重(w)和偏置(b),调整权重和偏置以便拟合训练数据的过程叫做“学习”;

个人理解:使用训练数据进行学习,调整参数,让模型预测得更准确,其中参数就是权重和偏置,准确度通过损失函数观察,该往什么方向调整通过损失函数的梯度决定;

神经网络学习步骤:mini-batch-->计算梯度-->更新参数;

2层神经网络代码实现思路:(TwoLayerNet类) 构造函数:提供初始化方法; predict函数:传入x作为第1层input值,计算数组乘积(参数通过构造函数初始化),使用sigmoid激活函数([0,input])计算出中间值;中间值作为第二层的input值,计算数组乘积(参数通过构造函数初始化),使用softmax函数(化为0.0-1.0间的值)作为输出函数计算输出值y; loss函数:返回CE损失函数计算后的值; accuracy函数:每经过一个epoch,就计算精度 gradient函数/numerical_gradient函数:计算梯度

2 详细内容

导入部分:

常用的特征量SIFT,SURF,HOG; 常用的分类器SVM,KNN; 机器学习中,一般使用测试数据与训练数据两部分进行学习和实验。首先,使用训练数据学习,寻找最优的参数,然后使用测试数据评价训练得到的模型的实际能力,获得泛化能力(就是处理未被观察的数据的能力); 训练数据又叫做监督数据; 过拟合:对某个数据集过度拟合的状态;

损失函数

寻找最优参数时,要寻找使损失函数的值尽可能小的参数;寻找的过程通过损失函数的导数实现;如果导数值为负,通过使参数向正方向改变,可以减少Loss的值,若导数值为正,通过使参数向负方向改变,可以减少Loss的值。

从纯数学的角度是比较好理解的,我们要找某个函数最小值,可以求导数找驻点,然后计算最小值;当有正参数参与计算时,可以求导数,改变参数的值以获得最小值; 作者从正反两个方向回答了why的问题,即精度是和阶跃函数都不能产生连续的变化(都是离散的,比如100笔中识别成功20笔,永远是离散的数据),所以改参数没什么用,而建立函数,求导数,更能了解数据走向;我们做数学题也是这个方法,看来这是人类解决问题的一个重要方法; 损失函数-均方误差

E=½∑(y-t)² #y是output值,t是监督数据,k是维数

看上去是基于方差的实现思想,方差本来就是度量随机变量 和其数学期望 (即 均值 )之间的偏离程度。代码直接return公式就行;

损失函数-交叉熵误差CE

E=-∑tlogy #y是Output,t是正确解标签 就是自然对数取反,因为这样结果是正数

实际实现时,为了避免溢出,会在Log里加一个微小值参数delta;代码实现也是直接return公式;

mini-batch学习 就是求大量数据的Loss,然后求这些loss-value的均值;书中给出了CEloss的批次处理公式;

E=-1/n∑∑tlogy

代码实现:

import numpy as np def cross_entropy_error(y, t): if y.ndim == 1:#y.ndim维度 t = t.reshape(1, t.size)#转换成1行size列 y = y.reshape(1, y.size)#转换成1行size列 # 监督数据是one-hot-vector的情况下,转换为正确解标签的索引 if t.size == y.size: t = t.argmax(axis=1)#数组最大索引号 batch_size = y.shape[0]#这种写法是行数 return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size #公式照抄 range单参数:0-batchsize....

梯度导入-数值微分-导数 数值微分 numerical differentiation 导数表示瞬间变化量,数学中通常是取极限;计算机不能直接求极限,实现方式是选择一个比较小的值,并且将计算方法转化成中心差分的形式计算,有误差是肯定的;

数值微分有误差,为了减少误差可以计算函数的差分。利用微小的差分求导数的过程叫做数值微分。使用过小的值会造成计算机出现溢出的问题(浮点数都是有位数的),所以微小值要选择合适的值;

梯度导入-数值微分-偏导数

有多个变量的函数的导数叫偏导数

书中给出的是固定一个变量,另一个变量转化成常量用求导数的方法求偏导; 梯度

由全部变量的偏导数汇总而成的变量称为梯度; 实际上,梯度会指向各处的函数值降低的方向。更严格地讲,梯度指示的方向是各点出的函数值减少最多的方向。

从代码上看,就是return了导数的数组;

代码实现:

#梯度 def numerical_gradient(f,x): h = 1e-4;#0.0001 grad = np.zeros_like(x) #生成和x相同的数组 for idx in range(x.size): tmp_val = x[idx] x[idx] = tmp_val + h fxh1 = f(x) x[idx] = tmp_val - h fxh2 = f(x)#这个写法好神奇啊 grad[idx] = (fxh1-fxh2)/(2*h)#差分公式 x[idx] = tmp_val return grad def function_2(x): return x[0]**2+x[1]**2#平方之和 grad = test.numerical_gradient(function_2,np.array([3.0,4.0])) print(grad) D:\software\Anaconda\python.exe D:\workspace_p\study\ch04\test.py [6. 8.] [6. 8.] Process finished with exit code 0

找最优参数是指损失函数最小值的参数。一般损失函数很复杂,使用梯度来寻找函数最小值的方法叫做梯度法;梯度为0的地方不一定是最小资,且当函数复杂并且呈扁平状时,学习可能进入停滞期; 虽然梯度不一定指向最小值,但是沿着梯度的方向可以最大限度减少函数的值。 梯度法中,函数的取值从当前位置沿着梯度前进,然后在新的地方重新求梯度,如此反复(类似迭代)。梯度法有梯度上升法和梯度下降法,梯度法主要是指下降法; 梯度法代码实现:

#梯度下降法 def gradient_descent(f,init_x,lr=0.01,step_num=100):#lr 学习率 x = init_x; for i in range(step_num): grad = numerical_gradient(f,x) x -= lr*grad#梯度法公式 x0-lr*偏导 就是让x动起来 return x #发现函数中可以不写文件名,但是在外部调用必须写 init_x = np.array([-3.0,4.0])#总是忘加[] grad = test.gradient_descent(function_2,init_x=init_x,lr=0.1,step_num=100) print(grad) D:\software\Anaconda\python.exe D:\workspace_p\study\ch04\test.py [-6.11110793e-10 8.14814391e-10] [-6.11110793e-10 8.14814391e-10] Process finished with exit code 0

学习率过大的话,会发散成很大的值,学习率过小,基本上没有怎么更新就结束了;像学习率这样的参数叫做超参数,是人工设定的;

代码实现(这章的代码对于我来说有点难以理解了,所以加了些注释): 两层神经网络:

# coding: utf-8 import sys, os sys.path.append(os.pardir) # 为了导入父目录的文件而进行的设定 from common.functions import * from common.gradient import numerical_gradient class TwoLayerNet: def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01): # 初始化权重 self.params = {} self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size) self.params['b1'] = np.zeros(hidden_size) self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size) self.params['b2'] = np.zeros(output_size)#创建1维数组 元素个数output_size def predict(self, x): W1, W2 = self.params['W1'], self.params['W2'] b1, b2 = self.params['b1'], self.params['b2'] a1 = np.dot(x, W1) + b1 z1 = sigmoid(a1)#cout[0,x] a2 = np.dot(z1, W2) + b2 y = softmax(a2)#cout 0.0-1.0 带e的那个公式 return y # x:输入数据, t:监督数据 def loss(self, x, t): y = self.predict(x) return cross_entropy_error(y, t) def accuracy(self, x, t): y = self.predict(x) y = np.argmax(y, axis=1)# 取最大值所在的索引作为预测结果 t = np.argmax(t, axis=1)# 取最大值所在的索引作为真实结果 accuracy = np.sum(y == t) / float(x.shape[0]) return accuracy # x:输入数据, t:监督数据 def numerical_gradient(self, x, t): loss_W = lambda W: self.loss(x, t) grads = {}#一开始以为是递归,点进去才发现是另一个计算梯度的函数 grads['W1'] = numerical_gradient(loss_W, self.params['W1']) grads['b1'] = numerical_gradient(loss_W, self.params['b1']) grads['W2'] = numerical_gradient(loss_W, self.params['W2']) grads['b2'] = numerical_gradient(loss_W, self.params['b2']) return grads # coding: utf-8 import sys, os sys.path.append(os.pardir) # 为了导入父目录的文件而进行的设定 import numpy as np import matplotlib.pyplot as plt from dataset.mnist import load_mnist from two_layer_net import TwoLayerNet#写法 # 读入数据 (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True) network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10) iters_num = 1000 # 适当设定循环的次数 train_size = x_train.shape[0] batch_size = 100 learning_rate = 0.1 train_loss_list = [] train_acc_list = [] test_acc_list = [] iter_per_epoch = max(train_size / batch_size, 1) #numpy.random.choice(a, size=None, replace=True, p=None) #从a(只要是ndarray都可以,但必须是一维的)中随机抽取数字,并组成指定大小(size)的数组 #replace:True表示可以取相同数字,False表示不可以取相同数字 #数组p:与数组a相对应,表示取数组a中每个元素的概率,默认为选取每个元素的概率相同。 for i in range(iters_num):#10000 batch_mask = np.random.choice(train_size, batch_size) x_batch = x_train[batch_mask]#dataset里的 t_batch = t_train[batch_mask] # 计算梯度 #grad = network.numerical_gradient(x_batch, t_batch) grad = network.gradient(x_batch, t_batch) #高速版 # 更新参数 for key in ('W1', 'b1', 'W2', 'b2'): network.params[key] -= learning_rate * grad[key] loss = network.loss(x_batch, t_batch) train_loss_list.append(loss) if i % iter_per_epoch == 0: #max(train_size / batch_size, 1) 每经过一个epoch,就计算识别精度 train_acc = network.accuracy(x_train, t_train)#成功数据百分比 test_acc = network.accuracy(x_test, t_test) train_acc_list.append(train_acc)#放入train_acc数组 test_acc_list.append(test_acc)#放入test_acc数组 print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc)) # 绘制图形 markers = {'train': 'o', 'test': 's'} x = np.arange(len(train_acc_list)) plt.plot(x, train_acc_list, label='train acc') plt.plot(x, test_acc_list, label='test acc', linestyle='--') plt.xlabel("epochs") plt.ylabel("accuracy") plt.ylim(0, 1.0) plt.legend(loc='lower right') plt.show()

end



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有